package com.example.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

@Configuration
public class GatewayConfig {

    @PostConstruct
    public void init(){
        initGatewayApiRules();
        initGatewayRules();
        initBlockRequestHandler();
        System.out.println("初始化成功GatewayConfig");
    }

    /**
     * 静态持久化方式的网关Api规则配置
     */
    private void initGatewayApiRules(){
        //创建一个api分组，并设置api分组名
        ApiDefinition apiDefinition = new ApiDefinition().setApiName("apiGroup");
        //创建一个匹配串列表
        Set<ApiPredicateItem> items = new HashSet<>();
        //添加一个匹配串
        items.add(
            new ApiPathPredicateItem()
                    //设置匹配模式，精准匹配、前缀匹配、正则表达式匹配
                    .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_REGEX)
                    //设置匹配串。详细理解，请参考正则表达式
                    .setPattern("/service1-call(/.*)*")
        );

        apiDefinition.setPredicateItems(items);
        Set<ApiDefinition> apiSet = new HashSet<>();
        apiSet.add(apiDefinition);
        //加载APi分组到sentinel控制台
        GatewayApiDefinitionManager.loadApiDefinitions(apiSet);
    }

    /**
     * 静态持久化方式的网关的流控规则配置
     */
    private void initGatewayRules(){
        Set<GatewayFlowRule> ruleSet = new HashSet<>();
        GatewayFlowRule flowRule = new GatewayFlowRule();
        GatewayParamFlowItem paramFlowItem = new GatewayParamFlowItem();
        //设置针对请求属性，PARAM_PARSE_STRATEGY_CLIENT_IP代表针对客户端ip地址进行流控
        paramFlowItem.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
                //设置请求属性的属性值匹配模式。PARAM_MATCH_STRATEGY_REGEX代表正则表达式
                .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX)
                //如果是针对header、url、cookie属性，则需要配置fieldName字段
//                .setFieldName("")
                //设置匹配串
                .setPattern("127.0.0.1");

        //设置APi类型，是路由id还是APi分组。
        flowRule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                //设置流控的api名称或者路由id
                .setResource("apiGroup")
                //设置限流阈值类型为QPS模式
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                //设置阈值
                .setCount(2)
                //统计时长，单位是秒
                .setIntervalSec(5)
                //设置针对请求的属性
                .setParamItem(paramFlowItem)
                //设置超过阈值后，不触发服务降级，而是当超过阈值+Burst时，才触发服务降级
                .setBurst(1);
        ruleSet.add(flowRule);
        //将流控规则添加到sentinel控制台
        GatewayRuleManager.loadRules(ruleSet);
    }

    /**
     * 当网关请求的微服务报错时，会进行服务降级
     * 自定义网关的服务降级处理。
     * 注意：
     * 1. 请求微服务出错是不会触发服务降级熔断，得另外配置
     */
    private void initBlockRequestHandler(){
        //自定义服务降级处理handler。
        BlockRequestHandler blockRequestHandler =  (serverWebExchange,throwabl)->{
            System.out.println("网关层服务降级处理");
            HashMap<String, Object> responseResult = new HashMap<>();
            responseResult.put("code",HttpStatus.INTERNAL_SERVER_ERROR);
            responseResult.put("data","网关层服务降级处理");
            Mono<ServerResponse> responseMono = ServerResponse.status(HttpStatus.NOT_ACCEPTABLE)
                        .contentType(MediaType.APPLICATION_JSON)
                        //设置响应体
                        .body(BodyInserters.fromValue(responseResult));
            return responseMono;
        };
        //网关的服务降级处理使用我们指定的服务降级处理handler
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}
